home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_56
/
dmastp11.asm
< prev
next >
Wrap
Assembly Source File
|
1995-01-01
|
20KB
|
508 lines
;══════════════════════════════════════════════════════════════════════════════
; Play with Cxh/Bxh commands on SoundBlaster 16/16ASP (here 16/stereo/unsigned)
; André Baresel (with some help from Craig Jackson)
;──────────────────────────────────────────────────────────────────────────────
; STATUS: DOES WORK ON SB16
; ■ sound crackles after a while - dounno yet what it is ...
; (somethings going wrong in CONVERT_HALF...)
;──────────────────────────────────────────────────────────────────────────────
; Requirements: 80286, SoundBlaster 16/16ASP (see BASEADDR,DMA channel,IRQ number)
; Resolutions : 16-bit / 4..44khz / stereo
; Note : ■ We use only 8 bit stereo data and convert it while playing
; into stereo 16bit, look at CONVERT_HALF (at the end of this file)
; ■ To creat a 8 bit stereo unsigned file do :
; "VOC2RAW TEST1.VOC /I /R"
;
; ■ DSP command 41h ... set sample rate
; ■ DSP command D5h ... Halt Autoinit 16 bit DMA operation
; ■ DSP command D6h ... Continue Autoinit 16 bit DMA operation
; ■ DSP command B6h 20h ... autoinit 16 bit stereo data with no sign
;
.MODEL small
.286
; CONSTANTS ───────────────────────────────────────────────────────────────────
; SoundBlaster SETUP
BASEADDR EQU 0220h ;SoundBlaster base address
DMAchannel EQU 5 ;SoundBlaster DMA channel
IRQ7 EQU 15 ;SoundBlaster IRQ
; PIC MASKS FOR MASK/DEMASK IRQ
PICANDMASK EQU 01111111b ;AND PIC mask for clear IRQ
PICORMASK EQU 10000000b ;OR PIC mask for set IRQ
; DMA CONTROLLER REGISTERS (16bit)
WRITEMASK EQU 0D4h ;WRITE MASK REGISTER
WRITEMODE EQU 0D6h ;WRITE MODE REGISTER
CLEARFLIPFLOP EQU 0D8h
PAGE16_CHN EQU 08Bh ;PAGE REGISTER FOR DMAchannel 5
BASE16_CHN EQU 0C4h ;BASEADDRESS REGISTER DMA 5
COUNT16_CHN EQU 0C6h ;COUNT REGISTER DMAchannel 5
; SAMPLERATE :
RATE EQU 02AEDh ; = 10989 Hz
; DMA MODE
WANTEDMODE EQU 01011000b ; singlemode, autoinit, readmode
; DMABuffer size :
DMABUFFERSIZE EQU 8*1024
;──────────────────────────────────────────────────────────────────────────────
; MACRO DEFINITIONs
;──────────────────────────────────────────────────────────────────────────────
STARTUP MACRO
; MASM 5.x COMPATIBILITY
__start: mov ax,DGROUP
mov ds,ax
mov bx,ss
sub bx,ax
shl bx,004h
mov ss,ax
add sp,bx
ENDM
WAITWRITE MACRO
LOCAL loopWait,endloop
; Arguments : DX = Status port (BASEADDR+0Ch)
; Returns : n/a
; Destroys : AL
push cx
xor cx,cx ; need that for slow SBs !
loopWait: dec cx
jz endloop
in al,dx ; AL = WRITE COMMAND STATUS
or al,al
js loopWait ; Jump if bit7=1 - writing not allowed
endloop: pop cx
ENDM
WAITREAD MACRO
LOCAL loopWait,endloop
; Arguments : DX = Status port (normaly BASEADDR+0Eh)
; Returns : n/a
; Destroys : AL
push cx
xor cx,cx ; need that for slow SBs !
loopWait: dec cx
jz endloop
in al,dx ; AL = DATA AVAILABLE STATUS
or al,al
jns loopWait ; Jump if bit7=0 - no data available
endloop: pop cx
ENDM
RESET_DSP MACRO
local SBthere
; Arguments : n/a
; Returns : n/a
; Destroys : DX,AL
mov dx,BASEADDR+06h
mov al,1
out dx,al ; start DSP reset
in al,dx
in al,dx
in al,dx
in al,dx ; wait 3 µsec
xor al,al
out dx,al ; end DSP Reset
add dx,08h ; dx = DSP DATA AVAILABLE
WAITREAD
sub dx,4 ; dx = DSP Read Data
in al,dx
cmp al,0aah ; if there is a SB then it returns 0AAh
je SBthere
jmp RESET_ERROR ; No SB - exit program
SBthere:
ENDM
;─── End of Macrodefinitions ──────────────────────────────────────────────────
.STACK 100h
.DATA
;──────────────────────────────────────────────────────────────────────────────
; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I /R" or creat your own
; textfile with sampledata
;
SAMPLEBUFFER LABEL BYTE
INCLUDE TEST1.INC
SAMPLEBUFFEREND LABEL BYTE
PART db 0
information db 13,10,'DMASTP11.EXE - play 16bit stereo data unsigned (that does only work on a'
db 13,10,'SB16/SB16ASP)'
db 13,10,'Pause playing with key "p" and continue it then with any key.'
db 13,10,'Stop playing with <ESC>.',10,'$'
memerror db 13,10,'Not enough memory to creat the DMA buffer','$'
txtpart0 db 13,'playing part 0','$'
txtpart1 db 13,'playing part 1','$'
sberror db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
OLDInterruptSEG dw ?
OLDInterruptOFS dw ?
; OFFSET AND PAGE FOR DMAC
DMAbufferOFS dw ?
DMAbufferPage db ?
; OFFSET AND SEGMENT FOR CPU ACCESS :)
DMABufferDOSOFS dw ?
DMABufferDOSSEG dw ?
; POSITION IN SAMPLEBUFFER WHILE CONVERTING
position dw 0
SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
;──────────────────────────────────────────────────────────────────────────────
.CODE
STARTUP
; FIRST FREE NOT USED MEMORY :
mov bx,ss
mov ax,es
sub bx,ax
mov ax,sp
add ax,15
shr ax,4
add bx,ax
mov ah,04ah
int 21h
; NOW ALLOCATE DMABUFFER
mov bx,DMABUFFERSIZE*2/16 ; count of 16byte blocks for two buffers
mov ah,48h
int 21h
jnc enoughmem
mov dx,offset memerror
mov ah,9
int 21h ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
jmp return2dos
enoughmem: ; AX = segment of DMA buffer / offset = 0
;──────────────────────────────────────────────────────────────────────────────
; calculate page and offset for 16bit DMAcontroller :
;
; segment*16+offset=20bit memory location-> upper 3 bits *2 = page
; next 16 bits = offset
; last 1 bit - lost in space :)
; (because of word access)
;──────────────────────────────────────────────────────────────────────────────
rol ax,4
mov bl,al
and bl,00eh
mov [DMAbufferPage],bl
and al,0f1h
ror ax,1
mov [DMABufferOFS],ax
;──────────────────────────────────────────────────────────────────────────────
; check for DMApage override :
; ... problem: DMA controller separates memory into 64KB pages, you can only
; transfer data is placed in one page - no page overrides are allowed
;──────────────────────────────────────────────────────────────────────────────
; To solve that :
; creat a DMA buffer with double size you want - if the first part is placed
; on a page border the second part is for sure not
;──────────────────────────────────────────────────────────────────────────────
mov cx,DMABUFFERSIZE/2 ; we check for 128KB pages and DMABUFFERSIZE
; in WORDs
neg ax ; ax = 65536 - ax (bytes left to DMA page border)
cmp ax,cx
ja nooverride
; USE SECOND PART :
neg ax ; ax = offset first data
add ax,cx ; use second part
mov [DMABufferOFS],ax
add [DMABufferPage],2 ; 2nd part is on next page !
nooverride:
;──────────────────────────────────────────────────────────────────────────────
; now fill the whole buffer with first words of data
; (2 times CALL CONVERT_HALF)
;
; but first - calculate the DOS SEG/OFS from the DMAPage/OFS (you know
; maybe we have to use second buffer half we don't know about ofs/seg yet)
mov al,byte ptr [DMABufferOFS]
and al,07h
xor ah,ah
shl ax,1
mov di,ax ; di = offset of DMAbuffer
mov ax,[DMABufferOFS]
and al,0f8h
mov bl,[DMABufferPage]
shr bl,1
or al,bl
ror ax,3
mov es,ax ; es = segment of DMABuffer
;──────────────────────────────────────────────────────────────────────────────
; save these values for later CONVERT_HALF calls
;
mov [DMABufferDOSOFS],di
mov [DMABufferDOSSEG],ax
xor ax,ax
; DS:SI - samples in dataseg
; ES:DI - DMABuffer
; fill the whole buffer with sample data
CALL CONVERT_HALF
CALL CONVERT_HALF
; NOW WE'RE READY FOR SB STUFF:
RESET_DSP
; WRITE INFOMRATION TO SCREEN :
mov dx,offset information
mov ah,9
int 21h ; write program information to screen
; ENABLE SB SPEAKERS (for all SBs <SB16)
mov dx,BASEADDR+00Ch ;DX = DSP Write Data or Command
WAITWRITE
mov al,0D1h ; AL = Enable speaker
out dx,al ; Output: DSP Write Data or Command
; SETUP IRQ :
xor ax,ax
mov es,ax ; es to page 0 (Interrupt table)
mov si,IRQ7*4 ; si = position in interrupt table
; DISABLE IRQ (if it was enabled somehow)
in al,021h
and al,PICANDMASK ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
out 021h,al
; CHANGE POINTER IN INTERRUPT TABLE
mov ax,es:[si]
mov [OLDInterruptOFS],ax ; save offset of old interupt vector for restoring
mov ax,OFFSET OWN_IRQ
mov es:[si],ax ; set offset of new interrupt routine
mov ax,es:[si+2]
mov [OLDInterruptSEG],ax ; save segment of old interupt vector for restoring
mov ax,cs
mov es:[si+2],ax ; set segment of new interrupt routine
; CHANGE PIC MASK :
in al,021h
and al,PICANDMASK ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
out 021h,al
mov cx,DMABUFFERsize/2-1 ; count of words we need :)
;──────────────────────────────────────────────────────────────────────────────
; Setup DMA-controller :
;
; 1st MASK DMA CHANNEL
;
mov al,DMAchannel-4 ; channels 0..3 for the 2nd DMAC
add al,4
out WRITEMASK,al
;──────────────────────────────────────────────────────────────────────────────
; 2nd CLEAR FLIPFLOP
;
out CLEARFLIPFLOP,al
;──────────────────────────────────────────────────────────────────────────────
; 3rd WRITE TRANSFER MODE
;
mov al,WANTEDMODE
add al,DMAchannel-4
out WRITEMODE,al
;──────────────────────────────────────────────────────────────────────────────
; 4th WRITE PAGE NUMBER
;
mov al,[DMAbufferPage]
out PAGE16_CHN,al
;──────────────────────────────────────────────────────────────────────────────
; 5th WRITE BASEADDRESS
;
mov ax,[DMABufferOFS]
out BASE16_CHN,al
mov al,ah
out BASE16_CHN,al
;──────────────────────────────────────────────────────────────────────────────
; 6th WRITE BUFFERLENGTH (in words)-1
;
mov al,cl
out COUNT16_CHN,al
mov al,ch
out COUNT16_CHN,al
;──────────────────────────────────────────────────────────────────────────────
; 7th DEMASK CHANNEL
;
mov al,DMAchannel-4
out WRITEMASK,al
;──────────────────────────────────────────────────────────────────────────────
; Setup SoundBlaster :
;
; 1st SET SAMPLERATE
;
mov dx,BASEADDR+00Ch ;DX = DSP Write Data or Command
WAITWRITE
mov al,041h ;AL = Set DAC Samplerate
out dx,al
WAITWRITE
mov cx,RATE
mov al,ch
out dx,al
WAITWRITE
mov al,cl
out dx,al
;──────────────────────────────────────────────────────────────────────────────
; 2nd USE 16bit STEREO UNSIGNED MODE (DSPC B6h 20h)
;
WAITWRITE
mov al,0B6h ;AL = DMA DAC 16bit autoinit
out dx,al
WAITWRITE
mov al,020h ;AL = stereo unsigned data
out dx,al
mov cx,DMABUFFERSIZE/4-1 ;half 16bit buffer
WAITWRITE
mov al,cl ;AL = LOWER PART SAMPLELENGTH
out dx,al
WAITWRITE
mov al,ch ;AL = HIGHER PART SAMPLELENGTH
out dx,al
; TRANSFER STARTs.....NOW.... :)
waitloop: mov ah,01 ;AH = Check for character function
int 016h ; Interrupt: Keyboard
jz waitloop ; wait for a key (sound in background)
xor ah,ah ;Read character, flush keypress
int 016h ; Interrupt: Keyboard
cmp al,'p' ; check for pause key
je pause ; ok
cmp al,27
jne waitloop
jmp exit
pause: ; NOW PAUSE PLAYING (on DSPv4.04 you can also use d0h,d4h)
mov dx,BASEADDR+00Ch ;DX = DSP Write Data or Command
WAITWRITE
mov al,0D5h
out dx,al
; WAIT FOR ANY KEY
xor ah,ah ;Read character, flush keypress
int 016h ; Interrupt: Keyboard
; CONTINUE PLAYING
mov dx,BASEADDR+00Ch ;DX = DSP Write Data or Command
WAITWRITE
mov al,0D6h
out dx,al
jmp waitloop
exit: RESET_DSP
; RESTORE PIC MASK
in al,021h
or al,PICORMASK ;<-- SET REGISTER MASK BITS TO DISABLE
out 021h,al
; RESTORE IRQ :
xor ax,ax
mov es,ax ; es to page 0 (Interrupt table)
mov si,IRQ7*4
mov ax,[OLDInterruptOFS]
mov es:[si],ax ; set old interrupt routine
mov ax,[OLDInterruptSEG]
mov es:[si+2],ax
; CLEAR KEYBUFFER
mov ah,01
int 16h
jz return2dos
xor ah,ah ;Read character, flush keypress
int 016h ; Interrupt: Keyboard
; TERMINATE EXE:
return2dos:
mov ax,04c00h
int 21h
; display information if Soundblaster is not on this baseaddress
RESET_ERROR:
mov dx,offset sberror
mov ah,9
int 21h ; text output
jmp return2dos
;──────────────────────────────────────────────────────────────────────────────
; Our own IRQ for detecting buffer half SB currently plays
; It's generated by the SoundBlaster hardware
;──────────────────────────────────────────────────────────────────────────────
OWN_IRQ: pusha
mov dx,BASEADDR+00Fh ;DX = IRQ ACKNOWLEDGE 16Bit
in al,dx
mov ax,@data
mov ds,ax
mov dx,offset txtpart0
cmp [part],0
je notpart1
mov dx,offset txtpart1
notpart1: mov ah,9
int 21h ; text output
call CONVERT_HALF ; fill next half...
mov al,020h
out 020h,al ;ACKNOWLEDGE HARDWARE INTERRUPT
popa
IRET
;──────────────────────────────────────────────────────────────────────────────
; Convert_half is for copying 8bit data from dataseg to dmabuffer with 16bit
; values (16bitvalue= 8bit value*256)
; one call - convert one buffer half
; next call - convert the other buffer half
; ... 2B Continued ...
; Note: no problem with stereo - it's stored like this :
; 1st byte for left,2nd for right,left,right .... etc.
; That's why we can forget about this while converting
;──────────────────────────────────────────────────────────────────────────────
CONVERT_HALF:
mov cx,DMABUFFERSIZE/2 ; half buffer size in bytes
mov di,[DMABufferDOSOFS]
cmp [part],0
je not2nd
add di,cx
not2nd: shr cx,1 ; count of words in half buffer
mov ax,[DMABufferDOSSEG]
mov es,ax
mov si,offset samplebuffer
add si,[position]
xor al,al
cloop: mov ah,ds:[si]
stosw
inc si
cmp si,offset samplebuffer + samplebufferlength-2
ja samplerestart
loop cloop
jmp afterloop
samplerestart:
; restart sample
mov si,offset samplebuffer
loop cloop
afterloop:
sub si,offset samplebuffer
mov [position],si
neg [part]
inc [part] ; part = 1-part result: 0,1,0,1,0,1,...
RET
END __start